package soot.jimple.toolkits.thread.mhp;


import soot.*;
import soot.jimple.*;
import soot.jimple.toolkits.thread.mhp.stmt.BeginStmt;
import soot.jimple.toolkits.thread.mhp.stmt.JPegStmt;
import soot.jimple.toolkits.thread.mhp.stmt.JoinStmt;
import soot.jimple.toolkits.thread.mhp.stmt.MonitorEntryStmt;
import soot.jimple.toolkits.thread.mhp.stmt.MonitorExitStmt;
import soot.jimple.toolkits.thread.mhp.stmt.NotifiedEntryStmt;
import soot.jimple.toolkits.thread.mhp.stmt.NotifyAllStmt;
import soot.jimple.toolkits.thread.mhp.stmt.NotifyStmt;
import soot.jimple.toolkits.thread.mhp.stmt.OtherStmt;
import soot.jimple.toolkits.thread.mhp.stmt.StartStmt;
import soot.jimple.toolkits.thread.mhp.stmt.WaitStmt;
import soot.jimple.toolkits.thread.mhp.stmt.WaitingStmt;
import soot.toolkits.scalar.*;
import soot.jimple.internal.*;
import soot.jimple.spark.sets.*;
import soot.jimple.spark.pag.*;
import soot.toolkits.graph.*;
import soot.jimple.toolkits.callgraph.*;
//import soot.util.cfgcmd.*;
import soot.util.*;

import java.util.*;

//add for add tag
import soot.tagkit.*;


// *** USE AT YOUR OWN RISK ***
// May Happen in Parallel (MHP) analysis by Lin Li.
// This code should be treated as beta-quality code.
// It was written in 2003, but not incorporated into Soot until 2006.
// As such, it may contain incorrect assumptions about the usage
// of certain Soot classes.
// Some portions of this MHP analysis have been quality-checked, and are
// now used by the Transactions toolkit.
//
// -Richard L. Halpert, 2006-11-30

// NOTE that this graph builder will only run to completion if all virtual
// method calls can be resolved to a single target method.  This is a severely
// limiting caveat.

public class PegChain extends HashChain{
	
	CallGraph callGraph;
	private final List heads = new ArrayList();
	private final List tails= new ArrayList() ;
	private final FlowSet pegNodes = new ArraySparseSet();
	
	private final Map<Unit, JPegStmt> unitToPeg = new HashMap<Unit, JPegStmt>();
	private final Map<String, FlowSet> waitingNodes;
	private final PegGraph pg;
	private final Set<List<Object>> joinNeedReconsidered = new HashSet<List<Object>>();
	public Body body; // body from which this peg chain was created
	// private Map startToThread;
	
	Hierarchy hierarchy;
	PAG pag;
	Set threadAllocSites;
	Set methodsNeedingInlining;
	Set allocNodes;
	List<List> inlineSites;
	Map<SootMethod, String> synchObj;
	Set multiRunAllocNodes;
	Map<AllocNode, String> allocNodeToObj;
	
	PegChain(CallGraph callGraph, Hierarchy hierarchy, PAG pag, Set threadAllocSites, Set methodsNeedingInlining, Set allocNodes, List<List> inlineSites, Map<SootMethod, String> synchObj, Set multiRunAllocNodes, Map<AllocNode, String> allocNodeToObj, Body unitBody, SootMethod sm,String threadName, boolean addBeginNode, PegGraph pegGraph)
	{
		this.allocNodeToObj = allocNodeToObj;
		this.multiRunAllocNodes = multiRunAllocNodes;
		this.synchObj = synchObj;
		this.inlineSites = inlineSites;
		this.allocNodes = allocNodes;
		this.methodsNeedingInlining = methodsNeedingInlining;
		this.threadAllocSites = threadAllocSites;
		this.hierarchy = hierarchy;
		this.pag = pag;
		this.callGraph = callGraph;
		body = unitBody;
		pg = pegGraph;
		waitingNodes = pegGraph.getWaitingNodes();
		//Find exception handlers
		Iterator trapIt = unitBody.getTraps().iterator();
		Set<Unit> exceHandlers = pg.getExceHandlers();
		while(trapIt.hasNext()) {
			Trap trap = (Trap) trapIt.next();
			Unit handlerUnit = trap.getHandlerUnit();
			exceHandlers.add(handlerUnit);
		}
		
		
		
		//System.out.println("entering buildPegChain");
		UnitGraph graph = new CompleteUnitGraph(unitBody);
		
		Iterator unitIt = graph.iterator();	
		//	HashMap unitToPeg = new HashMap((graph.size())*2+1,0.7f);
		//June 19 add begin node
		
		if (addBeginNode){
			//create PEG begin statement
			JPegStmt beginStmt =new BeginStmt("*", threadName, graph, sm);
			pg.getCanNotBeCompacted().add(beginStmt);
			addNode(beginStmt);
			heads.add(beginStmt);
			
			
		}
		//end June 19 add begin node
		
		Iterator it = graph.getHeads().iterator();
		
		while (it.hasNext()){
			Object head = it.next();
			//breadth first scan
			Set<Unit> gray = new HashSet<Unit>();
			LinkedList<Object> queue = new LinkedList<Object>();
			queue.add(head);
			
			visit((Unit)queue.getFirst(), graph, sm,
					threadName,  addBeginNode);
			while (queue.size()>0){
				Unit root = (Unit)queue.getFirst();
				
				Iterator succsIt = graph.getSuccsOf(root).iterator();
				while (succsIt.hasNext()){
					Unit succ = (Unit)succsIt.next();
					
					if (!gray.contains(succ)){
						gray.add(succ);
						queue.addLast(succ);
						visit(succ, graph,sm, threadName,  addBeginNode);
						
					}
				}
				queue.remove(root);
			}
			
		}
		
		postHandleJoinStmt();
		pg.getUnitToPegMap().put(this, unitToPeg);
		
	}
	
	private void visit(Unit unit, UnitGraph graph, 
			SootMethod sm,String threadName,  boolean addBeginNode)
	{
		/*
		 if (unit instanceof JIdentityStmt){
		 System.out.println("JIdentityStmt left: "+((JIdentityStmt)unit).getLeftOp());
		 System.out.println("JIdentityStmt right: "+((JIdentityStmt)unit).getRightOp());
		 }
		 */
		//System.out.println("unit: "+unit);
		if (unit instanceof MonitorStmt)
		{
			Value value =((MonitorStmt)unit).getOp();
			if (value instanceof Local)
			{
				Type type = ((Local)value).getType();
				
				if (type instanceof RefType)
				{
					
					SootClass sc = ((RefType)type).getSootClass();
					if (unit instanceof EnterMonitorStmt)
					{
						
						String objName = makeObjName(value, type, unit);
						
						JPegStmt pegStmt = new MonitorEntryStmt(objName,threadName,unit, 
								graph, sm);
						addAndPutNonCompacted(unit, pegStmt);
						return;
					}
					if (unit instanceof ExitMonitorStmt)
					{
						String objName = makeObjName(value, type, unit);
						JPegStmt pegStmt = new MonitorExitStmt(objName,threadName,unit,graph, sm);
						addAndPutNonCompacted(unit, pegStmt);
						
						return;
					}
				}//end if RefType
				
			}//end if Local
			
			
		}//end if MonitorStmt
		
		if (((Stmt)unit).containsInvokeExpr())
		{
			
			Value invokeExpr =((Stmt)unit).getInvokeExpr();
			
			SootMethod method = ((InvokeExpr)invokeExpr).getMethod();
			
			String name = method.getName();
			Value value = null;
			Type type = null;
			List paras = method.getParameterTypes();
			String objName = null;
			if (invokeExpr instanceof InstanceInvokeExpr)
			{ 
				
				
				value = ((InstanceInvokeExpr)invokeExpr).getBase();
				
				if (value instanceof Local)
				{
					//	Type type = ((Local)value).getType();
					type = ((Local)value).getType();
					
					if (type instanceof RefType)
					{
						
						SootClass sc = ((RefType)type).getSootClass();
						
						//sc = ((RefType)type).getSootClass();
						objName = sc.getName();
						
					}
				}
			}			   
			else
			{
				if (!(invokeExpr instanceof StaticInvokeExpr))
				{
					throw new RuntimeException("Error: new type of invokeExpre: "+invokeExpr);
				}
				else{
					//static invoke
				}
			}
			//Check if a method belongs to a thread.
			boolean find = false;
			if (method.getName().equals("start"))
			{
				//System.out.println("Test method is: "+method);
				//System.out.println("DeclaringClass: "+method.getDeclaringClass());
				List<SootClass> superClasses = hierarchy.getSuperclassesOfIncluding(method.getDeclaringClass());
				Iterator<SootClass> it = superClasses.iterator();
				
				while (it.hasNext())
				{
					String className = it.next().getName();
					if (className.equals("java.lang.Thread"))
					{
						find = true;
						break;
					}
				}
			}
			if (method.getName().equals("run") ){
				//System.out.println("method name: "+method.getName());
				//System.out.println("DeclaringClass name: "+method.getDeclaringClass().getName());
				if ((method.getDeclaringClass().getName()).equals("java.lang.Runnable")){
					//System.out.println("find: "+find);
					
					find = true;
				}
			}
			
			if (name.equals("wait") &&
					(paras.size() == 0 || 
							(paras.size() == 1 && (Type)paras.get(0) instanceof LongType )||
							(paras.size() == 2 && (Type)paras.get(0) instanceof LongType &&(Type)paras.get(1) instanceof IntType))){
				
				/*special modeling for wait() method call which
				 *transforms wait() node to 3 node.
				 */
				objName = makeObjName(value, type, unit);
				transformWaitNode(objName, name, threadName, unit, graph,sm);
			}
			
			else{
				if ((name.equals("start") || name.equals("run")) && find){
					
					
					// System.out.println("DeclaringClass: "+method.getDeclaringClass().getName());
					//System.out.println("====start method: "+method);
					//  System.out.println("unit: "+unit);
					List<AllocNode> mayAlias = null;
					PointsToSetInternal pts = (PointsToSetInternal) pag.reachingObjects((Local)value );
					mayAlias = findMayAlias(pts, unit);
					
					JPegStmt pegStmt = new StartStmt(value.toString(),threadName,unit, graph, sm);
					
					
					if (pg.getStartToThread().containsKey(pegStmt)){
						throw new RuntimeException("map startToThread contain duplicated start() method call");
					}
					pg.getCanNotBeCompacted().add(pegStmt);
					addAndPut(unit, pegStmt);
					List<PegChain> runMethodChainList = new ArrayList<PegChain>();
					List<AllocNode> threadAllocNodesList = new ArrayList<AllocNode>();
					//add Feb 01
					if (mayAlias.size() < 1 ){
						
						throw new RuntimeException("The may alias set of "+unit+"is empty!");
						
					}
					Iterator<AllocNode> mayAliasIt = mayAlias.iterator();
					//System.out.println("mayAlias: "+mayAlias);
					while (mayAliasIt.hasNext()){
						AllocNode allocNode = mayAliasIt.next();
						//System.out.println("allocNode toString: "+allocNode.toString());
						RefType refType = ((NewExpr)allocNode.getNewExpr()).getBaseType();
						SootClass maySootClass = refType.getSootClass();
						//remeber to modify here!!! getMethodByName is unsafe!
						//			if (method.getDeclaringClass()
						/*
						 TargetMethodsFinder tmd = new TargetMethodsFinder();
						 List targetList = tmd.find(unit, callGraph, false);
						 SootMethod meth=null;
						 if (targetList.size()>1) {
						 System.out.println("targetList: "+targetList);
						 throw new RuntimeException("target of start >1!");
						 }
						 else
						 meth = (SootMethod)targetList.get(0);
						 */
						SootMethod meth = hierarchy.resolveConcreteDispatch(maySootClass, method.getDeclaringClass().getMethodByName("run"));
						//System.out.println("==method is: "+meth);
						
						Body mBody = meth.getActiveBody();
						
						//Feb 2 modify thread name
						int threadNo = Counter.getThreadNo();
						String callerName = "thread"+threadNo;
//						System.out.println("Adding thread start point: " + "thread" + threadNo + " pegStmt: " + pegStmt);
						
						//map caller ()-> start pegStmt
						pg.getThreadNameToStart().put(callerName, pegStmt);
						PegChain newChain = new PegChain( callGraph, hierarchy, pag, threadAllocSites, methodsNeedingInlining, allocNodes, inlineSites, synchObj, multiRunAllocNodes, allocNodeToObj, mBody, sm, callerName, true, pg);
						
						pg.getAllocNodeToThread().put(allocNode, newChain);
						
						runMethodChainList.add(newChain);
						threadAllocNodesList.add(allocNode);
					}
					
					//end add Feb 01
					
//					System.out.println("Adding something to startToThread");
					pg.getStartToThread().put(pegStmt,runMethodChainList);
					pg.getStartToAllocNodes().put(pegStmt,threadAllocNodesList);
					
					
					
				}//end if (name.equals("start") )
				else
				{
					if (name.equals("join") && method.getDeclaringClass().getName().equals("java.lang.Thread"))
					{
						
						//If the may-alias of "join" has more that one elements, we can NOT kill anything.
						PointsToSetInternal pts = (PointsToSetInternal) pag.reachingObjects((Local)value );
						//System.out.println("pts: "+pts);
						List<AllocNode> mayAlias = findMayAlias(pts, unit);
						
						//System.out.println("=====mayAlias for thread: "+unit +" is:\n"+mayAlias);
						
						if (mayAlias.size() != 1){
							if (mayAlias.size() <1){
								//System.out.println("===points to set: "+pts);
								//System.out.println("the size of mayAlias <0 : \n"+mayAlias);	
								throw new RuntimeException("==threadAllocaSits==\n"+threadAllocSites.toString());
								
							}
							
							JPegStmt pegStmt = new JoinStmt(value.toString(), threadName, unit, graph, sm);
							
							addAndPutNonCompacted(unit, pegStmt);
							pg.getSpecialJoin().add(pegStmt);
							
						}
						else {
							
							Iterator<AllocNode> mayAliasIt = mayAlias.iterator();
							
							while (mayAliasIt.hasNext()){
								
								AllocNode allocNode = mayAliasIt.next();
								//System.out.println("allocNode toString: "+allocNode.toString());
								JPegStmt pegStmt = new JoinStmt(value.toString(), threadName,
										unit, graph, sm);
								if (!pg.getAllocNodeToThread().containsKey(allocNode)){
									List<Object> list = new ArrayList<Object>();
									list.add(pegStmt);
									list.add(allocNode);
									list.add(unit);
									joinNeedReconsidered.add(list);
									//throw new RuntimeException("allocNodeToThread does not contains key: "+allocNode);
								}
								else{//If the mayAlias contains one 1 element, then use the threadName as
									// the Obj of the JPegStmt.
									//String callerName = (String)allocNodeToCaller.get(allocNode);
									Chain thread = pg.getAllocNodeToThread().get(allocNode);
									
									addAndPutNonCompacted(unit, pegStmt);
									pg.getJoinStmtToThread().put(pegStmt, thread);
									
								}
							}
							
						}
						
					}
					else
					{
						//June 17 add for build obj->notifiyAll map.
						if (name.equals("notifyAll") &&  paras.size() == 0 )
						{
							objName = makeObjName(value, type, unit);
							JPegStmt pegStmt = new NotifyAllStmt(objName, threadName, unit, graph, sm);
							
							addAndPutNonCompacted(unit, pegStmt);
							//build notifyAll Map
							if (pg.getNotifyAll().containsKey(objName)){
								Set<JPegStmt> notifyAllSet = pg.getNotifyAll().get(objName);
								notifyAllSet.add(pegStmt);
								pg.getNotifyAll().put(objName, notifyAllSet);
							}
							else{
								Set<JPegStmt> notifyAllSet = new HashSet<JPegStmt>();
								notifyAllSet.add(pegStmt);
								pg.getNotifyAll().put(objName, notifyAllSet);
							}
							
							//end build notifyAll Map
							
							
						}
						else{
							
							//add Oct 8, for building pegs with inliner.
							if ( name.equals("notify") &&
									paras.size() == 0 && 
									method.getDeclaringClass().getName().equals("java.lang.Thread") ){
								objName = makeObjName(value, type, unit);
								JPegStmt pegStmt = new NotifyStmt(objName, threadName, unit, graph, sm);
								addAndPutNonCompacted(unit, pegStmt);
							}
							
							else{
								//				    //System.out.println("******method before extend: "+method); 
								// System.out.println("isConcretemethod: "+method.isConcrete());
								// System.out.println("isLibraryClass: "+method.getDeclaringClass().isLibraryClass());
								if (method.isConcrete() && !method.getDeclaringClass().isLibraryClass()){
									
									List<SootMethod> targetList = new LinkedList<SootMethod>();
									SootMethod targetMethod = null;
									if (invokeExpr instanceof StaticInvokeExpr){
										targetMethod = method;
									}
									else{
										TargetMethodsFinder tmd = new TargetMethodsFinder();
										targetList = tmd.find(unit, callGraph, true, false);
										
										
										
										if (targetList.size() > 1){
											System.out.println("target: "+targetList);
											System.out.println("unit is: "+unit);
											System.err.println("exit because target is bigger than 1.");
											System.exit(1); // What SHOULD be done is that all possible targets are inlined 
															// as though each method body is in a big switch on the type of
															// the receiver object.  The infrastructure to do this is not
															// currently available, so instead we exit.  Continuing would
															// yield wrong answers.
										}
										else if(targetList.size() < 1){
											System.err.println("targetList size <1");
											//					    System.exit(1);
											//continue;
										}
										else
											targetMethod = targetList.get(0);
									}
									
									if (methodsNeedingInlining == null)
									{
										System.err.println("methodsNeedingInlining is null at " + unit);
									}
									else if (targetMethod == null)
									{
										System.err.println("targetMethod is null at " + unit);
									}
									else if (methodsNeedingInlining.contains(targetMethod))
									{					
										inlineMethod(targetMethod, objName, name, 
												threadName,unit, graph, 
												sm);
									}
									else
									{
										JPegStmt pegStmt = new OtherStmt(objName,name,threadName,
												unit, graph, sm);
										addAndPut(unit, pegStmt);
									}
								}
								
								else{
									JPegStmt pegStmt = new OtherStmt(objName,name,threadName,
											unit, graph, sm);
									addAndPut(unit, pegStmt);
									
								}
								
								
							}
							
							//end add Oct 8, for building pegs with inliner.
							
						}
					}
				}
				
				
				
			}//end else if ("wait")
			
		}// end if containsInvokeExpr()
		
		else{
			
			newAndAddElement( unit, graph, threadName, sm);	
		}
		
		
		
		
	}
	//end buildPegChain()
	
	
	
	
	private void transformWaitNode(String objName, String name, String threadName, Unit unit, UnitGraph graph,
			SootMethod sm){
		JPegStmt pegStmt = new WaitStmt(objName,threadName,unit, graph, sm);
		
		addAndPutNonCompacted(unit, pegStmt);
		
		JPegStmt pegWaiting = new WaitingStmt(objName, threadName, sm);
		pg.getCanNotBeCompacted().add(pegWaiting);
		addNode(pegWaiting);
		//build waitingNodesMap
		if (waitingNodes.containsKey(objName)){
			FlowSet waitingNodesSet = waitingNodes.get(objName);
			if (!waitingNodesSet.contains(pegWaiting)){
				waitingNodesSet.add(pegWaiting);
				waitingNodes.put(pegWaiting.getObject(), waitingNodesSet);
				//System.out.println("get a waiting nodes set");
			}
			else{
				//throw an run time exception
			}
		}
		else{
			FlowSet waitingNodesSet = new ArraySparseSet();
			waitingNodesSet.add(pegWaiting);
			waitingNodes.put(pegWaiting.getObject(), waitingNodesSet);
			//System.out.println("new a waiting nodes set");
		}
		//end build waitingNodes Map
		
		{
			List successors = new ArrayList();
			successors.add(pegWaiting);
			pg.getUnitToSuccs().put(pegStmt, successors);
		}
		
		JPegStmt pegNotify = new NotifiedEntryStmt(objName, threadName, sm);
		pg.getCanNotBeCompacted().add(pegNotify);
		addNode(pegNotify);
		
		{
			List successors = new ArrayList();
			successors.add(pegNotify);
			pg.getUnitToSuccs().put(pegWaiting, successors);
		}
	}
	private List<AllocNode> findMayAlias(PointsToSetInternal pts, Unit unit){
		// returns a list of reaching objects' AllocNodes that are contained in the set of known AllocNodes
		List<AllocNode> list = new ArrayList<AllocNode>();
		Iterator<AllocNode> it =  makePtsIterator(pts);
		while (it.hasNext()){
			AllocNode obj = it.next();
			
				list.add(obj);
		}
		return list;
	}
	
	private void inlineMethod(SootMethod targetMethod,  String objName, String name, String threadName,
			Unit unit, UnitGraph graph,  SootMethod sm ){
		//System.out.println("inside extendMethod "+ targetMethod);
		
		Body unitBody = targetMethod.getActiveBody();
		
		JPegStmt pegStmt = new OtherStmt(objName,name,threadName,unit, graph,sm );
		
		if (targetMethod.isSynchronized()){
			// System.out.println(unit+" is synchronized========");
			
			String synchObj = findSynchObj(targetMethod);
			JPegStmt enter = new MonitorEntryStmt(synchObj, threadName, graph, sm);
			JPegStmt exit = new MonitorExitStmt(synchObj,  threadName,  graph, sm);
			pg.getCanNotBeCompacted().add(enter);
			pg.getCanNotBeCompacted().add(exit);
			
			List list = new ArrayList();
			list.add(pegStmt);
			list.add(enter);
			list.add(exit);
			//System.out.println("add list to synch: "+list);
			pg.getSynch().add(list);
		}
		addAndPut(unit, pegStmt);
		
		
		PegGraph pG = new PegGraph( callGraph, hierarchy, pag, methodsNeedingInlining, allocNodes, inlineSites, synchObj, multiRunAllocNodes, allocNodeToObj, unitBody, threadName, targetMethod,true,false );
//		pg.addPeg(pG, this); // RLH
		//PegToDotFile printer1 = new PegToDotFile(pG, false, targetMethod.getName());
		//System.out.println("NeedInlining for "+targetMethod +": "+pG.getNeedInlining());
		
		//if (pG.getNeedInlining()){
		List list = new ArrayList();
		list.add(pegStmt);
		list.add(this);
		list.add(pg);
		list.add(pG);
		inlineSites.add(list);
		//System.out.println("----add list to inlineSites !---------");
		//}
		
	}
	private String findSynchObj(SootMethod targetMethod){
		
		if (synchObj.containsKey(targetMethod)){
			return synchObj.get(targetMethod);
		}
		else{
			String objName = null;
			if (targetMethod.isStatic()){
				objName = targetMethod.getDeclaringClass().getName();
			}
			else{
				Iterator it = ((Chain)(targetMethod.getActiveBody()).getUnits()).iterator();
				
				while (it.hasNext()){
					Object obj = it.next();
					if (obj instanceof JIdentityStmt){
						Value thisRef = ((JIdentityStmt)obj).getLeftOp();
						if (thisRef instanceof Local){
							Type type = ((Local)thisRef).getType();
							if (type instanceof RefType){
								objName = makeObjName(thisRef, type, (Unit)obj);
								synchObj.put(targetMethod, objName);
								break;
							}
						}
					}
				}
			}
			return objName;
		}
	}
	private void addNode(JPegStmt stmt){
		this.addLast(stmt);
		pegNodes.add(stmt);
		pg.getAllNodes().add(stmt);
	}
	private void addAndPut(Unit unit, JPegStmt stmt){
		unitToPeg.put(unit,stmt);
		addNode(stmt);
	}
	private void addAndPutNonCompacted(Unit unit, JPegStmt stmt){
		pg.getCanNotBeCompacted().add(stmt);
		addAndPut(unit, stmt);
	}
	private void newAndAddElement( Unit unit, UnitGraph graph,
			String threadName,SootMethod sm ){
		JPegStmt pegStmt = new OtherStmt("*",unit.toString(),threadName,unit, graph, sm);
		addAndPut(unit, pegStmt);
	}
	
	
	
	public List getHeads()
	{
		return heads;
	}
	public List getTails()
	{
		return tails;
	}
	protected void addTag(){
		//add tag for each stmt
		Iterator it = iterator();
		while (it.hasNext()){
			JPegStmt stmt = (JPegStmt)it.next();
			int count = Counter.getTagNo();
			StringTag t = new StringTag(Integer.toString(count));
			stmt.addTag(t);
		}
	}    
	private Iterator<AllocNode> makePtsIterator(PointsToSetInternal pts){
		final HashSet<AllocNode> ret = new HashSet<AllocNode>();
		pts.forall( new P2SetVisitor() {
			public void visit( Node n ) {
				
				ret.add( (AllocNode)n );
			}
		} );
		//testPtsIterator(ret.iterator());
		return ret.iterator();
	}
	/*  public List getPredsOf(Object s)
	 {
	 if(!unitToPreds.containsKey(s))
	 throw new RuntimeException("Invalid stmt" + s);
	 
	 return (List) unitToPreds.get(s);
	 }
	 
	 public List getSuccsOf(Object s)
	 {
	 
	 if(!unitToSuccs.containsKey(s))
	 throw new RuntimeException("Invalid stmt:" + s);
	 
	 return (List) unitToSuccs.get(s);
	 }
	 */
	//Sometimes, we can not find the target of join(). Now we should handle it.
	private void postHandleJoinStmt(){
		Iterator<List<Object>> it = joinNeedReconsidered.iterator();
		while (it.hasNext()){
			List list = it.next();
			JPegStmt pegStmt = (JPegStmt)list.get(0);
			AllocNode allocNode = (AllocNode)list.get(1);
			Unit unit =(Unit)list.get(2);
			if (!pg.getAllocNodeToThread().containsKey(allocNode)){
				
				throw new RuntimeException("allocNodeToThread does not contains key: "+allocNode);
			}
			else{//If the mayAlias contains one 1 element, then use the threadName as
				// the Obj of the JPegStmt.
				//String callerName = (String)allocNodeToCaller.get(allocNode);
				Chain thread = pg.getAllocNodeToThread().get(allocNode);
				
				addAndPutNonCompacted(unit, pegStmt);
				pg.getJoinStmtToThread().put(pegStmt, thread);
				
			}
		}
	}
	private String makeObjName(Value value, Type type, Unit unit){
		//System.out.println("unit: "+unit);
		PointsToSetInternal pts = (PointsToSetInternal) pag.reachingObjects((Local)value );
		//System.out.println("pts for makeobjname: "+pts);
		List<AllocNode> mayAlias = findMayAlias(pts, unit);
		
		String objName =null;
		if (allocNodeToObj == null) throw new RuntimeException("allocNodeToObj is null!");
		
		if (mayAlias.size() == 1){
			//System.out.println("unit: "+unit);
			
			AllocNode an = mayAlias.get(0);
			// System.out.println("alloc node: "+an);
//			if (!multiRunAllocNodes.contains(an)){
			
			if (allocNodeToObj.containsKey(an)){
				objName = allocNodeToObj.get(an);
			}
			else{
				//System.out.println("===AllocNodeToObj does not contain key allocnode: "+an);
				//objName = type.toString()+Counter.getObjNo();
				objName = "obj"+Counter.getObjNo();
				allocNodeToObj.put(an, objName);
			}
			//System.out.println("objName: "+objName);
//			}
//			else
//			throw new RuntimeException("The size of object corresponds to site "+ unit + " is not 1.");
			
			
		}
		else{
			AllocNode an = mayAlias.get(0);
			// System.out.println("alloc node: "+an);
//			if (!multiRunAllocNodes.contains(an)){
			
			if (allocNodeToObj.containsKey(an)){
				objName = "MULTI" + allocNodeToObj.get(an);
			}
			else{
				//System.out.println("===AllocNodeToObj does not contain key allocnode: "+an);
				//objName = type.toString()+Counter.getObjNo();
				objName = "MULTIobj"+Counter.getObjNo();
				allocNodeToObj.put(an, objName);
			}
			//System.out.println("objName: "+objName);
//			}
//			else
//			throw new RuntimeException("The size of object corresponds to site "+ unit + " is not 1.");
//			System.out.println("pts: "+pts);
//			throw new RuntimeException("The program exit because the size of object corresponds to site "+ unit + "is not 1.");
		}
		//System.out.println("==return objName: "+objName);
		if (objName == null) throw new RuntimeException("Can not find target object for "+unit);
		return objName;
	}
	protected Map<String, FlowSet> getWaitingNodes(){
		return waitingNodes;
	}
	protected void testChain()
	{
		System.out.println("******** chain********");
		Iterator it = iterator();
		while (it.hasNext()) {
			
			JPegStmt stmt =(JPegStmt)it.next(); 
			System.out.println(stmt.toString());
			
		}
		
	}
}
